package com.tyut.musicdreamer.framework.starter.web.advice;

import cn.hutool.core.collection.CollectionUtil;
import com.tyut.musicdreamer.framework.starter.convention.exception.AbstractException;
import com.tyut.musicdreamer.framework.starter.convention.respstatus.RespStatusEnum;
import com.tyut.musicdreamer.framework.starter.convention.result.Result;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;

import java.util.Optional;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.tyut.musicdreamer")
public class GlobalExceptionHandlerAdvice {

    /**
     * 拦截参数验证异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());
        String exceptionStr = Optional.ofNullable(firstFieldError)
                .map(FieldError::getDefaultMessage)
                .orElse("参数验证失败");
        log.warn("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);
        return new Result<>()
                .setCode(RespStatusEnum.CLIENT_BAD_PARAMETERS.code())
                .setMessage(exceptionStr);
    }

    /**
     * 拦截文件上传大小超限异常
     */
    @ExceptionHandler(value = {MaxUploadSizeExceededException.class, MultipartException.class})
    @ResponseStatus(HttpStatus.OK)
    public Result<?> uploadException(HttpServletRequest request, Exception ex) {
        log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), ex.getMessage());
        return new Result<>()
                .setCode(RespStatusEnum.CLIENT_BAD_PARAMETERS.code())
                .setMessage("上传文件过大，请控制在50MB以内");
    }

    /**
     * 拦截应用内抛出的异常
     */
    @ExceptionHandler(value = {AbstractException.class})
    @ResponseStatus(HttpStatus.OK)
    public Result<?> abstractException(HttpServletRequest request, AbstractException ex) {
        log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), ex.toString(), ex);
        return new Result<>()
                .setCode(ex.getErrorCode())
                .setMessage(ex.getErrorMessage());
    }

    /**
     * 拦截未捕获异常
     */
    @ExceptionHandler(value = Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> defaultErrorHandler(HttpServletRequest request, Throwable throwable) {
        log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);
        return new Result<>()
                .setCode(RespStatusEnum.SERVICE_ERROR.code())
                .setMessage(throwable.getMessage());
    }

    private String getUrl(HttpServletRequest request) {
        if (StringUtils.isEmpty(request.getQueryString())) {
            return request.getRequestURL().toString();
        }
        return request.getRequestURL().toString() + (request.getQueryString() == null ? "" : "?" + request.getQueryString());
    }
}
